package com.evaluation.service.impl;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.evaluation.dao.config.EvalJdbcTemplate;
import com.evaluation.dto.QueryParamsDto;
import com.evaluation.security.Md5PwdEncoder;
import com.evaluation.security.PwdEncoder;
import com.evaluation.service.api.IUserService;
import com.evaluation.util.ServiceResult;
import com.google.common.collect.Maps;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class UserServiceImpl extends EvalJdbcTemplate implements IUserService {
	
	@Override
	public boolean updatePassword( String userId, String password, String newPassword ) {

		PwdEncoder md5 = new Md5PwdEncoder();
		
		Map<String, String> paramMap = new HashMap<>();
		
		paramMap.put("userId", userId);
		paramMap.put("password",  md5.encodePassword(password, userId));
		paramMap.put("newPassword", md5.encodePassword(newPassword,userId));
		
		int ret = jdbc.update("update tb_userinfo set password=:newPassword WHERE user_id=:userId AND password=:password", paramMap);
		
		if( ret < 1 ) {
			return false;
		}
		
		return true;
	}
	
	@Override
	public boolean loginLog( String userId, String loginIp ) {
		
		Map<String, String> param = new HashMap<>();
		param.put("userId", userId);
		param.put("loginIp", loginIp);
		//param.put("loginTime", DateUtil.format( new Date(), "yyyy-MM-dd HH:mm:ss"));
		
		jdbc.update("insert into tb_login_log ( user_id, login_ip, login_time ) values ( :userId, :loginIp, now() );", param);
		
		return true;
	}
	
	@Override
	public boolean addUser( String userId, String userName, String password, String userProjectId, String gender ) {

		Map<String, String> params = Maps.newHashMap();
		params.put("userId", userId);
		
		Map<String, Object> map = jdbc.queryForMap("select count(1) cnt from tb_userinfo where user_id=:userId", params);
		
		if( map != null && map.size() > 0 && (long)map.get("cnt") > 0 ) {
			return false;
		}
		
		PwdEncoder md5 = new Md5PwdEncoder();
		
		Map<String, Object> paramMap = new HashMap<>();
		
		paramMap.put("userId", userId);
		paramMap.put("password", !StringUtils.hasLength(password) ? null : md5.encodePassword(password, userId));
		paramMap.put("userName", userName);
		paramMap.put("gender", gender);
		paramMap.put("roleId", "02");
		paramMap.put("createDate", new Date());
		
		int ret = jdbc.update("insert into tb_userinfo (user_id, user_name, password, role_id, gender, status, create_date) values ( :userId, :userName, :password, :roleId, :gender, '1', :createDate )", paramMap);
		
		if( ret < 1 ) {
			return false;
		}
		
		paramMap.put("projectId", userProjectId);
		
		return 0 < jdbc.update("insert into tb_project_user ( project_id, user_id ) values ( :projectId, :userId)", paramMap);
		
	}
	
	@Override
	public Map<String, Object> getUserinfoByUserId( String userId) {
		String sql = "select * from tb_userinfo where user_id=:userId ";
		
		Map<String, String> params = Maps.newHashMap();
		params.put("userId", userId);
		
		Map<String, Object> map = jdbc.queryForMap(sql, params);
		/*if( map != null && map.get("create_date") != null ) {
			map.put( "create_date",  DateUtil.format( (LocalDateTime)map.get("create_date"), "yyyy-MM-dd HH:mm:ss") );
			
		}*/
		
		List<Map<String, Object>> roleList = queryAllUserRole();
		Map<Object, Object> roleMap = roleList.stream().collect( Collectors.toMap( r -> r.get("role_id"), v->v.get("role_name") ) );
		
		String[] roleIds = ((String)map.get("role_id")).split("\\,");
		String[] roleStr = new String[roleIds.length];
		for ( int i=0; i<roleIds.length; i++ ) {
			String roleId = roleIds[i];
			
			roleStr[i] = (String)roleMap.get( roleId );
		}
		
		map.put("roleName", String.join(", ", roleStr));
		
		return map;
	}
	
	@Override
	public boolean resetPassword( String userId, String newPassword ) {
		PwdEncoder md5 = new Md5PwdEncoder();
		Map<String, String> paramMap = new HashMap<>();
		
		paramMap.put("userId", userId);
		paramMap.put("newPassword", md5.encodePassword(newPassword, userId));
		
		int ret = jdbc.update("update tb_userinfo set password=:newPassword WHERE user_id=:userId ", paramMap);
		
		if( ret < 1 ) {
			return false;
		}
		
		return true;
	}
	
	@Override
	public ServiceResult<List<Map<String, Object>>> queryAllUserInfo( QueryParamsDto queryParamsDto, Map< String, String > param ) {
		ServiceResult<List<Map<String, Object>>> sr = new ServiceResult<>();
		Map<String, Object> paramMap = new HashMap<>();
		
		StringBuilder cond = new StringBuilder(" WHERE 1=1 ");
		if (StringUtils.hasLength(param.get("userId"))) {
			cond.append( " AND user_id like :userId " );
			paramMap.put( "userId" , "%"+param.get("userId").trim()+"%" );
		}
		if (StringUtils.hasLength(param.get("userName"))) {
			cond.append( " AND user_name like :userName " );
			paramMap.put( "userName" , "%"+param.get("userName").trim()+"%" );
		}
		if (StringUtils.hasLength(param.get("status"))) {
			cond.append( " AND status = :status " );
			paramMap.put( "status" , param.get("status") );
		}
		if (StringUtils.hasLength(param.get("showLinksFlag"))) {
			cond.append( " AND show_links_flag = :showLinksFlag " );
			paramMap.put( "showLinksFlag" , param.get("showLinksFlag") );
		}
		
		Integer count = jdbc.queryForObject(" select count(1) cnt from tb_userinfo "+cond.toString(), paramMap, Integer.class);
		if( count != null && count.intValue() > 0 ) {
			sr.setTotal( count.intValue() );
		}
		
		paramMap.put( "start", queryParamsDto.getOffset() );
		paramMap.put( "end", queryParamsDto.getLimit() );
		
		String sql = """
				select user_id userId, user_name userName, role_id role, gender, status,
				create_date createDate, login_time loginTime, login_number loginNumber, email,
				jira_name jiraName, firms, log_flag logFlag, show_links_flag showLinksFlag,
				profiles, img_url imgUrl from tb_userinfo 
				""" +  cond + " order BY role_id, id asc ";
		if ( queryParamsDto.getOffset() != -1 && queryParamsDto.getLimit() != -1 ) {
			sql += " limit :start,:end ";
		}
		
		log.info( sql.toString() );
		sr.setResult( jdbc.queryForList(sql.toString(), paramMap) );
		return sr;
	}

	@Override
	public ServiceResult<List<Map<String, Object>>> queryProjectUserByUserId(String userId) {
		ServiceResult<List<Map<String, Object>>> sr = new ServiceResult<>();
		Map<String, String> param = new HashMap<>();
		param.put("userId", userId);
		
		String sql = """
				SELECT u.user_id userId, u.project_id projectId, p.project_name projectName, p.jira_name jiraName, chandao_id chandaoID
				FROM tb_project_user u JOIN tb_project p ON p.project_id=u.project_id WHERE u.user_id=:userId
				""";
		
		log.info( sql.toString() );
		sr.setResult( jdbc.queryForList(sql.toString(), param) );
		return sr;
	}
	
	@Override
	public ServiceResult<List<Map<String, Object>>> getAllProject() {
		ServiceResult<List<Map<String, Object>>> sr = new ServiceResult<>();
		
		sr.setResult( jdbc.queryForList("SELECT p.id, p.project_name projectName, p.jira_name jiraName FROM tb_project p WHERE p.status='1'", new HashMap<>() ) );
		return sr;
	}

	@Override
	public ServiceResult<String> saveProjectUser(Map<String, String> param) {
		ServiceResult<String> sr = new ServiceResult<>();
		Map<String, String> paramMap = new HashMap<>();
		paramMap.put("userId", param.get("userId"));
		
		jdbc.update( "DELETE FROM tb_project_user WHERE user_id=:userId", paramMap);
		
		String proList = param.get("proList");
		String[] pro = proList.split(",");
		for (String str : pro) {
			paramMap.put("projectId", str);
			jdbc.update( "INSERT INTO tb_project_user ( project_id, user_id ) VALUES ( :projectId, :userId )" , paramMap);
		}
		return sr;
	}

	@Override
	public void updateUserLoginTime(Object userId, String loginIp) {
		Map<String, Object> param = new HashMap<>();
		param.put("loginTime", new Date());
		param.put("userId", userId);
		
		StringBuilder querySql = new StringBuilder(" SELECT login_number loginNumber FROM tb_userinfo WHERE user_id=:userId ");
		List<Map<String, Object>> userList = jdbc.queryForList(querySql.toString(), param);
		int loginNumber = 0;
		for ( Map<String, Object> map : userList ) {
			if ( map.get("loginNumber") != null ) {
				loginNumber = Integer.valueOf( map.get("loginNumber").toString() );
			}
			loginNumber++;
		}
		
		param.put("loginNumber", loginNumber);
		
		jdbc.update( "update tb_userinfo set login_time=:loginTime, login_number=:loginNumber WHERE user_id=:userId" , param);
		
		loginLog( userId.toString(), loginIp );
	}
	
	@Override
	public ServiceResult<String> saveUser(Map<String, String> param) {
		ServiceResult<String> sr = new ServiceResult<>();
		
		log.info(param.toString());
		
		String sql = """ 
				update tb_userinfo set user_name=:userName, gender=:gender, role_id=:roleList, status=:status,
				email=:email, jira_name=:jiraName, firms=:firms, log_flag=:logFlag, show_links_flag=:showLinksFlag,
				profiles=:profiles WHERE user_id=:userId
				""";
		
		jdbc.update( sql.toString() , param);
		
		//{gender=2, jiraName=, logFlag=, checkRO=02, firms=, userName=朱娟, roleList=02,06, userId=june.zhu, email=june.zhu@libertymutual.com.cn, status=1}
		
		return sr;
	}

	@Override
	public ServiceResult<List<Map<String, Object>>> queryLoginLogInfo(QueryParamsDto queryParamsDto, Map<String, String> param) {
		ServiceResult<List<Map<String, Object>>> sr = new ServiceResult<>();
		Map<String, Object> paramMap = new HashMap<>();
		
		StringBuilder cond = new StringBuilder(" WHERE 1=1 ");
		if (StringUtils.hasLength(param.get("userId"))) {
			cond.append( " AND user_id like :userId " );
			paramMap.put( "userId" , "%"+param.get("userId").trim()+"%" );
		}
		if (StringUtils.hasLength(param.get("loginIp"))) {
			cond.append( " AND login_ip like :loginIp " );
			paramMap.put( "loginIp" , "%"+param.get("loginIp").trim()+"%" );
		}
		if (StringUtils.hasLength(param.get("loginTime"))) {
			cond.append( " AND login_time between :loginTime and :loginTime2 " );
			paramMap.put( "loginTime" , param.get("loginTime")+" 00:00:00" );
			paramMap.put( "loginTime2" , param.get("loginTime")+" 23:59:59" );
		}
		
		Integer count = jdbc.queryForObject(" select count(1) cnt from tb_login_log "+cond.toString(), paramMap, Integer.class);
		if( count != null && count.intValue() > 0 ) {
			sr.setTotal( count.intValue() );
		}
		
		paramMap.put( "start", queryParamsDto.getOffset() );
		paramMap.put( "end", queryParamsDto.getLimit() );
		StringBuilder sql = new StringBuilder(" select id, user_id userId, login_ip loginIp, login_time loginTime ");
		sql.append( " from tb_login_log " );
		sql.append( cond );
		sql.append( " order by id desc limit :start,:end " );
		log.info( sql.toString() );
		sr.setResult( jdbc.queryForList(sql.toString(), paramMap) );
		return sr;
	}

	@Override
	public ServiceResult<List<Map<String, Object>>> queryLogMessage(Map<String, String> param) {
		ServiceResult<List<Map<String, Object>>> sr = new ServiceResult<>();
		StringBuilder sql = new StringBuilder(" select id, user_id userId, content, reply_user_id replyUser, reply_id replyId, type, create_date createDate ");
		sql.append(" from tb_log_message where status='1' and log_id=:logId ");
		sr.setResult( jdbc.queryForList(sql.toString(), param) );
		return sr;
	}

	@Override
	public ServiceResult<String> addLogMsg(Map<String, String> param) {
		ServiceResult<String> sr = new ServiceResult<>();
		log.info("{}", param );
		if ( StringUtils.hasLength(param.get("replyId")) && !"null".equals(param.get("replyId")) ) {
			jdbc.update("insert into tb_log_message ( log_id, user_id, content, reply_user_id, reply_id, type, create_date, status ) values "
					+ " ( :logId, :userId, :content, :replyUser, :replyId, :type, now(), '1' )", param );
		} else {
			jdbc.update("insert into tb_log_message ( log_id, user_id, content, reply_user_id, type, create_date, status ) values "
					+ " ( :logId, :userId, :content, :replyUser, :type, now(), '1' )", param );
		}
		return sr;
	}
	
	@Override
	public List<Map<String, Object>> queryUsers(Map<String, String> param) {
		Map<String, String> paramMap = new HashMap<>();
		StringBuilder sql = new StringBuilder(" SELECT user_id userId, user_name userName, jira_name jiraName ");
		sql.append(" FROM tb_userinfo WHERE status='1' AND ( role_id like '%02%' OR role_id like '%01%' ) ");
		if (StringUtils.hasLength(param.get("user"))) {
			sql.append( " AND ( user_id like :user OR user_name like :user ) " );
			paramMap.put( "user" , "%"+param.get("user").trim()+"%" );
		}
		return jdbc.queryForList( sql.toString(), paramMap );
	}

	@Override
	public ServiceResult<Map<String, List<Map<String, Object>>>> queryUserAndModuleByProject(Map<String, String> param) {
		ServiceResult<Map<String, List<Map<String, Object>>>> sr = new ServiceResult<>();
		
		String sql = """
				SELECT pu.user_id userId, u.user_name userName FROM tb_project_user pu
				LEFT JOIN tb_userinfo u ON u.user_id=pu.user_id WHERE u.status='1' AND pu.project_id=:projectId
					""";
		
		if( "issue".equals( param.get("type") ) ) {
			sql += """
					union
					select user_id userId, user_name userName from tb_userinfo where status='1'
					and FIND_IN_SET('03', role_id)
					""";
		}

		Map<String, List<Map<String, Object>>> mapData = new HashMap<>();
		
		mapData.put( "user", jdbc.queryForList(sql, param) );
		
		sql = """
				SELECT module_id AS moduleId, module_name AS moduleName FROM tb_project_module
				WHERE project_id = :projectId ORDER BY id; 
				""";
		
		mapData.put("module", jdbc.queryForList(sql, param));
		
		sr.setResult( mapData );
		
		return sr;
	}

	@Override
	@Cacheable(cacheNames="wLog-Cache", key="#root.method", unless="#result == null")
	public List<Map<String, Object>> queryAllUserRole() {
		return jdbc.queryForList("select * from tb_user_role ", new HashMap<>());
	}
	
	@Override
	public List<Map<String, Object>> queryAllUser() {
		String sql = """
				SELECT user_id userId, user_name userName, jira_name jiraName FROM tb_userinfo 
				WHERE NOT FIND_IN_SET('00', role_id) and status='1'
				""";
		return jdbc.queryForList( sql, new HashMap<>() );
	}

	@Override
	public Map<Object, Object> queryIndexData(String userId) {
		
		Map<String, String> paramMap = new HashMap<>();
		
		paramMap.put("userId", userId);
		
		String sql = """
				SELECT 'logNum' AS type, count(1) cnt FROM tb_loginfo WHERE status='1' AND user_id = :userId
				UNION
				SELECT 'buildNum' AS type, count(1) cnt FROM tb_jenkins_log WHERE start_time >= curdate()
				UNION
				SELECT 'issueNum' AS type, count(1) cnt FROM tb_issue_track WHERE create_date > date_sub( now(), interval 1 year );
				""";
		
		List<Map<String, Object>> dataList = jdbc.queryForList(sql, paramMap);
		
		Map<Object, Object> dataMap = dataList.stream().collect( Collectors.toMap( r -> r.get("type"), v->v.get("cnt") ) );
		
		QueryParamsDto queryParamsDto = new QueryParamsDto();
		queryParamsDto.setOffset( 0 );
		queryParamsDto.setLimit( 8 );
		
		paramMap.clear();
		ServiceResult<List<Map<String, Object>>> sr = queryLoginLogInfo( queryParamsDto, paramMap );
		 
		dataMap.put("login", sr.getResult());
		
		return dataMap;
	}

	@Override
	public void updateUserImgUrl(Map<String, String> param) {
		log.info(param.toString());
		
		jdbc.update( "update tb_userinfo set img_url=:imgUrl WHERE user_id=:userId" , param );
	}

	@Override
	public List<Map<String, Object>> queryITTeamUsers() {
		String sql = """
			select * from tb_userinfo where FIND_IN_SET(role_id,'01,02,03') and `password` is null and `status`='1' ORDER BY user_name asc
		""";
		return jdbc.queryForList( sql, new HashMap<>() );
	}

	@Override
	public ServiceResult<Map<String,List<?>>> statisticalLoginLog( String userId ) {

		String sql = """
				select DATE_FORMAT(login_time, '%Y%m') m, count(1) cnt from tb_login_log
					where user_id = :userId and login_time > DATE_SUB( now(),INTERVAL 1 year)
					group by m
				""";
		List<Map<String, Object>> list = jdbc.queryForList(sql, Map.of("userId", userId));
		
		List<String> xdata = list.stream().map( m->{ return (String)m.get("m"); } ).collect( Collectors.toList() );

		List<Long> ydata = list.stream().map( m->{ return (Long)m.get("cnt"); } ).collect( Collectors.toList() );
				
		ServiceResult<Map<String,List<?>>> sr = new ServiceResult<>();
		
		sr.setResult( Map.of("x", xdata, "y", ydata) );
		
		return sr;
	}

}
